package com.gcstorage.tvdev.net;


import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.gcstorage.tvdev.base.BaseApplication;
import com.gcstorage.tvdev.bean.SplitFileBean;
import com.gcstorage.tvdev.bean.SplitResBean;
import com.gcstorage.tvdev.util.FileUploadSplitUtil;
import com.gcstorage.tvdev.util.MD5Util;
import com.gcstorage.tvdev.util.SpUtils;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.model.Progress;
import com.lzy.okgo.model.Response;


import java.io.File;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import top.zibin.luban.Luban;


/**
 * 网络请求
 * Created by Sanpark on 2019/4/29 0029 11:23
 */
public class HttpClient {
    private static HttpClient instance;

    public static HttpClient get() {
        if (instance == null) {
            instance = new HttpClient();
        }
        return instance;
    }

    private HttpClient() {
    }

    private Observable<JsonElement> request(Observable<JsonElement> observable) {
        return observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public void get(String url, Map<String, Object> req, RxObserver observer) {
        get(url, req).subscribe(observer);
    }

    public void get(String url, Map<String, Object> req, Map<String, Object> headers, RxObserver observer) {
        get(url, req, headers).subscribe(observer);
    }

    public void post(String url, Map<String, Object> req, RxObserver observer) {
        post(url, req).subscribe(observer);
    }

    public void post(String url, Map<String, Object> req, Map<String, Object> headers, RxObserver observer) {
        post(url, req, headers).subscribe(observer);
    }

    public void postBody(String url, Object req, RxObserver observer) {
        postBody(url, req).subscribe(observer);
    }

    public void postBody(String url, Object req, Map<String, Object> headers, RxObserver observer) {
        postBody(url, req, headers).subscribe(observer);
    }

    public void postBody(String url, String req, RxObserver observer) {
        postBody(url, req).subscribe(observer);
    }

    public void postBody(String url, String req, Map<String, Object> headers, RxObserver observer) {
        postBody(url, req, headers).subscribe(observer);
    }

    public void uploadPic(String url, Map<String, Object> map, List<String> pathList, RxObserver observer) {
        uploadPic(url, map, pathList).subscribe(observer);
    }

    public void uploadPic(String url, Map<String, Object> map, Map<String, Object> headers, List<String> pathList, RxObserver observer) {
        uploadPic(url, map, pathList, headers).subscribe(observer);
    }

    public void uploadFile(String url, Map<String, Object> map, List<String> pathList, RxObserver observer) {
        uploadFile(url, map, pathList).subscribe(observer);
    }

    public void uploadFireFile(String url, Map<String, Object> map, List<String> pathList, RxObserver observer) {
        uploadFireFile(url, map, pathList).subscribe(observer);
    }

    public void uploadFile(String url, Map<String, Object> map, Map<String, Object> headers, List<String> pathList, RxObserver observer) {
        uploadFile(url, map, pathList, headers).subscribe(observer);
    }

    public Observable<JsonElement> get(String url, Map<String, Object> req) {
        return request(RetrofitManager.getInstance().createApiService().get(url, req));
    }

    public Observable<JsonElement> get(String url, Map<String, Object> req, Map<String, Object> headers) {
        return request(RetrofitManager.getInstance().createApiService().get(url, req, headers));
    }

    public Observable<JsonElement> post(String url, Map<String, Object> req) {
        return request(RetrofitManager.getInstance().createApiService().post(url, req));
    }

    public Observable<JsonElement> post(String url, Map<String, Object> req, Map<String, Object> headers) {
        return request(RetrofitManager.getInstance().createApiService().post(url, req, headers));
    }

    public Observable<JsonElement> postBody(String url, String req) {
        return request(RetrofitManager.getInstance().createApiService().post(url, RequestBody.create(MediaType.parse("application/json;charset=utf-8"), req)));
    }

    public Observable<JsonElement> postBody(String url, String req, Map<String, Object> headers) {
        return request(RetrofitManager.getInstance().createApiService().post(url, RequestBody.create(MediaType.parse("application/json;charset=utf-8"), req), headers));
    }

    public Observable<JsonElement> postBody(String url, Object req) {
        return request(RetrofitManager.getInstance().createApiService().post(url, RequestBody.create(MediaType.parse("application/json;charset=utf-8"), new Gson().toJson(req))));
    }

    public Observable<JsonElement> postBody(String url, Object req, Map<String, Object> headers) {
        return request(RetrofitManager.getInstance().createApiService().post(url, RequestBody.create(MediaType.parse("application/json;charset=utf-8"), new Gson().toJson(req)), headers));
    }

    public Observable<JsonElement> uploadPic(String url, Map<String, Object> map, List<String> pathList) {
        return uploadPic(url, map, pathList, Collections.emptyMap());
    }

    public Observable<JsonElement> uploadPic(String url, Map<String, Object> map, List<String> pathList, Map<String, Object> headers) {
        return Observable.just(pathList)
                .observeOn(Schedulers.io())
                .map(new Function<List<String>, List<File>>() {
                    @Override
                    public List<File> apply(@NonNull List<String> strings) throws Exception {
                        return Luban.with(BaseApplication.getInstance()).load(pathList).ignoreBy(100).get();
                    }
                }).map(new Function<List<File>, List<String>>() {
                    @Override
                    public List<String> apply(@NonNull List<File> files) throws Exception {
                        ArrayList<String> list = new ArrayList<>();
                        for (int i = 0; i < files.size(); i++) {
                            list.add(files.get(i).getPath());
                        }
                        return list;
                    }
                }).flatMap(new Function<List<String>, ObservableSource<JsonElement>>() {
                    @Override
                    public ObservableSource<JsonElement> apply(@NonNull List<String> strings) throws Exception {
                        return request(RetrofitManager.getInstance().createApiService().upload(url, map, covertFilePath(strings, MediaType.parse("image/jpeg")).build().parts(), headers));
                    }
                });
    }

    public Observable<JsonElement> uploadFile(String url, Map<String, Object> map, List<String> pathList) {
        if (FileUploadSplitUtil.fileOverLimit(pathList)) {
            return request(uploadSpitFile(url.replace("/upload", "/uploadSlice"), map, pathList));
        } else {
            return request(RetrofitManager.getInstance().createApiService().upload(url, map, covertFilePath(pathList, MediaType.parse("multipart/form-data")).build().parts()));
        }
    }

    public Observable<JsonElement> uploadFireFile(String url, Map<String, Object> map, List<String> pathList) {
        return request(RetrofitManager.getInstance().createApiService().upload(url, map, covertFireFilePath(pathList, MediaType.parse("multipart/form-data")).build().parts()));
    }

    public Observable<JsonElement> uploadYFile(String url, Map<String, Object> map, List<String> pathList, String parentId) {
        return request(RetrofitManager.getInstance().createApiService().upload(url, map, covertYFilePath(pathList, parentId, MediaType.parse("multipart/form-data")).build().parts()));
    }

    public Observable<JsonElement> uploadFile(String url, Map<String, Object> map, List<String> pathList, Map<String, Object> headers) {
        return request(RetrofitManager.getInstance().createApiService().upload(url, map, covertFilePath(pathList, MediaType.parse("multipart/form-data")).build().parts(), headers));
    }

    //将文件路径转化成MultipartBody.Builder
    private MultipartBody.Builder covertFilePath(List<String> pathList, MediaType mediaType) {
        MultipartBody.Builder multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
        for (int i = 0; i < pathList.size(); i++) {
            File file = new File(pathList.get(i));
            RequestBody body = RequestBody.create(mediaType, file);
            multipartBody.addFormDataPart("fileName" + (i + 1), file.getName(), body);
        }
        return multipartBody;
    }

    //将文件路径转化成MultipartBody.Builder
    private MultipartBody.Builder covertYFilePath(List<String> pathList, String parentId, MediaType mediaType) {
        MultipartBody.Builder multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
        File file = new File(pathList.get(0));
        RequestBody body = RequestBody.create(mediaType, file);
        multipartBody.addFormDataPart("file", file.getName(), body);
        multipartBody.addFormDataPart("fileName", file.getName());
        multipartBody.addFormDataPart("parentId", parentId);
        return multipartBody;
    }


    //将文件路径转化成MultipartBody.Builder
    private MultipartBody.Builder covertFireFilePath(List<String> pathList, MediaType mediaType) {
        MultipartBody.Builder multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
        for (int i = 0; i < pathList.size(); i++) {
            File file = new File(pathList.get(i));
            RequestBody body = RequestBody.create(mediaType, file);
            multipartBody.addFormDataPart("file" + (i + 1), file.getName(), body);
            multipartBody.addFormDataPart("fileList", file.getName());
        }
        return multipartBody;
    }

    //将byte[]转化为MultipartBody.Builder,不是完整的文件
    private MultipartBody.Builder coverBytes(String fileName, byte[] data, int index, MediaType mediaType) {
        MultipartBody.Builder multipartBody = new MultipartBody.Builder().setType(MultipartBody.FORM);
        RequestBody body = RequestBody.create(mediaType, data);
        multipartBody.addFormDataPart("file", fileName, body);
        return multipartBody;
    }

    public void downloadFile(String url, String fileName, String targetDir, DownloadProgressListener listener) {
        FileUtils.createOrExistsDir(targetDir);
        OkGo.<File>get(url).tag(this).execute(new FileCallback(targetDir, fileName) {
            @Override
            public void onError(Response<File> response) {
                super.onError(response);
                listener.onError(response.code(), response.message());
                ToastUtils.showShort(String.format("下载失败:%s", response.getException().getMessage()));
            }
            //            @Override
//            public void onSuccess(Response<File> response) {
//                listener.onSuccess();
//            }
//
//            @Override
//            public void onError(Response<File> response) {
//                super.onError(response);
//                listener.onError(response.code(), response.message());
//                ToastUtils.showShort(String.format("下载失败:%s", response.getException().getMessage()));
//            }

            @Override
            public void onSuccess(com.lzy.okgo.model.Response<File> response) {
                listener.onSuccess();
            }

            @Override
            public void onFinish() {
                super.onFinish();
                listener.onFinish();
            }

            @Override
            public void downloadProgress(Progress progress) {
                super.downloadProgress(progress);
                listener.update(progress.currentSize, progress.currentSize, progress.fraction == 1);

            }
        });
    }

    /**
     * 文件分片上传
     *
     * @param url      url
     * @param map      map
     * @param pathList 图片地址
     * @return 上传结果
     */
    private Observable<JsonElement> uploadSpitFile(String url, Map<String, Object> map, List<String> pathList) {
        //分割文件
        ArrayList<SplitFileBean> splitFiles = new FileUploadSplitUtil().getSpiltFiles(pathList);

        ArrayList<Observable<JsonElement>> observables = new ArrayList<>(splitFiles.size());

        for (int i = 0; i < splitFiles.size(); i++) {
            SplitFileBean splitFile = splitFiles.get(i);
            HashMap<String, Object> hashMap = new HashMap<>(map);
            hashMap.put("sliceTotal", splitFile.getPartCount());
            hashMap.put("sliceCurr", splitFile.getIndex());
            hashMap.put("digitalSign", MD5Util.md5(SpUtils.getString("tenantId") + splitFile.getTime() + splitFile.getFileName()));
            hashMap.put("fileName", splitFile.getFileName());
            hashMap.put("uploadTime", splitFile.getTime());
            Observable<JsonElement> observable = RetrofitManager.getInstance().createApiService().upload(url, hashMap, coverBytes(splitFile.getFileName(), splitFile.getData(), i, MediaType.parse("multipart/form-data")).build().parts());
            observables.add(observable);
        }
        //组装最终上传成功的返回数据
        List<SplitResBean> resData = new ArrayList<>(pathList.size());

        Observable<JsonElement> zipArray = Observable.zipArray(new Function<Object[], JsonElement>() {

            @Override
            public JsonElement apply(@androidx.annotation.NonNull Object[] objects) throws Exception {

                JsonElement jsonRes = null;

                JsonElement[] elements = Arrays.stream(objects).toArray(JsonElement[]::new);

                for (int i = 0; i < elements.length; i++) {
                    JsonElement element = elements[i];
                    Type type = new TypeToken<HttpResult<SplitResBean>>() {
                    }.getType();
                    HttpResult<SplitResBean> apiResBean = new Gson().fromJson(elements[i], type);
                    if (!apiResBean.isSuccess()) {
                        //直接返回错误信息
                        return element;
                    }
                    if (apiResBean.getData().isFull()) {
                        //上传成功文件添加到成功集合中
                        resData.add(apiResBean.getData());
                    }
                    if (i + 1 == elements.length) {
                        //如果到最后还没有失败信息，文件为全部上传成功
                        HashMap<String, Object> resMap = new HashMap<>(3);
                        resMap.put("code", apiResBean.getCode());
                        resMap.put("msg", apiResBean.getMsg());
                        resMap.put("data", resData);
//                        jsonRes = JsonParser.parse(new Gson().toJson(resMap));
                    }
                }
                return jsonRes;
            }

        }, false, splitFiles.size(), observables.toArray(new Observable[0]));

        return zipArray;
    }

}
